package auxiliar;

import io.Reader;
import io.Writer;

import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.Random;

import midi.Constantes;
import midi.Nota;
import midi.Notas;
import midi.Player;

public class Mapeamentos
{
	public static Player mapeamentoPorLinha(WritableRaster raster,
			int[] instrumentosSelecionados) {
		int numeroDeColunas = raster.getWidth(); // Largura da imagem
		int numeroDeLinhas = raster.getHeight(); // Altura da imagem
		int somaR = 0;
		int somaG = 0;
		int somaB = 0;
		float[] rgb = new float[3];

		Notas notas1 = new Notas();
		Notas notas2 = new Notas();
		Notas notas3 = new Notas();
		ArrayList<Integer> instrumentos = new ArrayList<Integer>();

		// Preenchimento das matrizes
		for (int i = 0; i < numeroDeLinhas; i++)
		{
			for (int j = 0; j < numeroDeColunas; j++)
			{
				raster.getPixel(j, i, rgb);
				somaR += (char) rgb[0];
				somaG += (char) rgb[1];
				somaB += (char) rgb[2];
			}

			notas1.add(new Nota(rgbParaAltura(somaR / numeroDeColunas),
					Constantes.SEMINIMA, 70));
			notas2.add(new Nota(rgbParaAltura(somaG / numeroDeColunas),
					Constantes.COLCHEIA, 70));
			notas2.add(new Nota(0, Constantes.COLCHEIA, 0));
			System.out.println("R: " + rgbParaAltura(somaR / numeroDeColunas)
					+ "  G: " + rgbParaAltura(somaG / numeroDeColunas)
					+ "  B: " + rgbParaAltura(somaB / numeroDeColunas));
			notas3.add(new Nota(rgbParaAltura(somaB / numeroDeColunas),
					Constantes.SEMICOLCHEIA, 70));
			notas3.add(new Nota(0, Constantes.SEMICOLCHEIA, 0));
			notas3.add(new Nota(0, Constantes.SEMICOLCHEIA, 0));
			notas3.add(new Nota(0, Constantes.SEMICOLCHEIA, 0));

			somaR = somaG = somaB = 0;

		}

		for (int a = 0; a < 3; a++)
			instrumentos.add(instrumentosSelecionados[a
					% (instrumentosSelecionados.length)]);

		ArrayList<Notas> notasFaixas = new ArrayList<Notas>();
		notasFaixas.add(notas1);
		notasFaixas.add(notas2);
		notasFaixas.add(notas3);

		// Inicializa o player:
		return new Player(notasFaixas, instrumentos);

	}

	public static Player mapeamentoPorColuna(WritableRaster raster,
			int[] instrumentosSelecionados) {

		int numeroDeColunas = raster.getWidth(); // Largura da imagem
		int numeroDeLinhas = raster.getHeight(); // Altura da imagem
		int somaR = 0;
		int somaG = 0;
		int somaB = 0;
		float[] rgb = new float[3];

		Notas notas1 = new Notas();
		Notas notas2 = new Notas();
		Notas notas3 = new Notas();
		ArrayList<Integer> instrumentos = new ArrayList<Integer>();

		// Preenchimento das matrizes
		for (int i = 0; i < numeroDeColunas; i++)
		{
			for (int j = 0; j < numeroDeLinhas; j++)
			{
				raster.getPixel(i, j, rgb);
				somaR += (char) rgb[0];
				somaG += (char) rgb[1];
				somaB += (char) rgb[2];
			}

			notas1.add(new Nota(rgbParaAltura(somaR / numeroDeLinhas),
					Constantes.SEMINIMA, 70));
			notas2.add(new Nota(rgbParaAltura(somaG / numeroDeLinhas),
					Constantes.COLCHEIA, 70));
			notas2.add(new Nota(0, Constantes.COLCHEIA, 0));
			System.out.println("R: " + rgbParaAltura(somaR / numeroDeLinhas)
					+ "  G: " + rgbParaAltura(somaG / numeroDeLinhas) + "  B: "
					+ rgbParaAltura(somaB / numeroDeLinhas));
			notas3.add(new Nota(rgbParaAltura(somaB / numeroDeLinhas),
					Constantes.SEMICOLCHEIA, 70));
			notas3.add(new Nota(0, Constantes.SEMICOLCHEIA, 0));
			notas3.add(new Nota(0, Constantes.SEMICOLCHEIA, 0));
			notas3.add(new Nota(0, Constantes.SEMICOLCHEIA, 0));

			somaR = somaG = somaB = 0;

		}
		for (int a = 0; a < 3; a++)
			instrumentos.add(instrumentosSelecionados[a
					% (instrumentosSelecionados.length)]);

		ArrayList<Notas> notasFaixas = new ArrayList<Notas>();
		notasFaixas.add(notas1);
		notasFaixas.add(notas2);
		notasFaixas.add(notas3);

		// Inicializa o player:
		return new Player(notasFaixas, instrumentos);

	}

	public static Player mapeamentoZeta(WritableRaster raster, int fator,
			int instrumento) {
		int numeroDeColunas = raster.getWidth(); // Largura da imagem
		int numeroDeLinhas = raster.getHeight(); // Altura da imagem
		int nota, duracao, intensidade; // Valores tempor�rios para soma
		int zeta = (fator * 2) + 1; // Vari�vel brutal zeta.
		// Vari�vel tempor�ria que recebe a informa��o do pixel.
		float[] rgb = new float[3];

		// Lista com as faixas simult�neas:
		ArrayList<Notas> notasFaixas = new ArrayList<Notas>();

		// Lista com as notas de uma faixa:
		Notas notas = new Notas();

		// Lista de instrumentos para cada faixa:
		ArrayList<Integer> instrumentos = new ArrayList<Integer>();

		// La�o que varre cada pixel da imagem e garante que n�o tente varrer
		// fora da �rea da imagem, mesmo deixando de pegar valores:
		int limiteLinhas = (int) Math.round(numeroDeLinhas / zeta);
		int limiteColunas = (int) Math.round(numeroDeColunas / zeta);

		for (int i = 0; i < limiteLinhas; i = i + fator) {
			for (int j = 0; j < limiteColunas; j = j + fator) {
				nota = 0;
				duracao = 0;
				intensidade = 0;

				// La�o que varre a vizinhan�a do pixel:
				for (int k = 0; k < zeta; k++) {
					for (int l = 0; l < zeta; l++) {
						raster.getPixel(k + i, l + j, rgb);
						nota += rgb[0];
						duracao += rgb[1];
						intensidade += rgb[2];
					}
				}

				// C�lculo da m�dia da vizinhan�a
				int divisor = (zeta) * (zeta);
				nota = Math.round(nota / divisor);
				duracao = Math.round(duracao / divisor);
				intensidade = Math.round(intensidade / divisor);

				// Normaliza os valores para �udio:
				nota = normalizaNota(nota);
				duracao = normalizaDuracao(duracao);
				intensidade = normalizaIntensidade(intensidade);
				notas.add(new Nota(nota, Constantes.getTempo(duracao),
						intensidade));
			}
		}
		notasFaixas.add(notas);
		instrumentos.add(instrumento);

		return new Player(notasFaixas, instrumentos);
	}

	public static Player mapeamentoTheta(WritableRaster raster, int fator,
			ArrayList<Integer> instrumentos) {
		// Largura da imagem:
		int numeroDeColunas = raster.getWidth();

		// Altura da imagem:
		int numeroDeLinhas = raster.getHeight();

		// Valores temporários para soma:
		int nota, duracao, intensidade;

		// Variável brutal zeta:
		int zeta = (fator * 2) + 1;

		// Quantidade de faixas simultaneas:
		int numeroDeFaixas = instrumentos.size();

		// Variável temporária que recebe a informação do pixel:
		float[] rgb = new float[3];

		// Lista com as faixas simultaneas:
		ArrayList<Notas> notasFaixas = new ArrayList<Notas>();

		// Lista com as notas de uma faixa:
		Notas notas = new Notas();

		// Laço que varre cada pixel da imagem e garante que não tente varrer
		// fora da área da imagem, mesmo deixando de pegar valores:
		int limiteLinhas = (int) Math.round(numeroDeLinhas / zeta);
		int limiteColunas = (int) Math.round(numeroDeColunas / zeta);

		for (int i = 0; i < limiteLinhas; i = i + fator) {
			for (int j = 0; j < limiteColunas; j = j + fator) {
				nota = 0;
				duracao = 0;
				intensidade = 0;

				// Laço que varre a vizinhança do pixel:
				for (int k = 0; k < zeta; k++) {
					for (int l = 0; l < zeta; l++) {
						raster.getPixel(k + i, l + j, rgb);
						nota += rgb[0];
						duracao += rgb[1];
						intensidade += rgb[2];
					}
				}

				// Cálculo da media da vizinhança
				int divisor = (zeta) * (zeta);
				nota = Math.round(nota / divisor);
				duracao = Math.round(duracao / divisor);
				intensidade = Math.round(intensidade / divisor);

				// Normaliza os valores para áudio:
				nota = normalizaNota(nota);
				duracao = normalizaDuracao(duracao);
				intensidade = normalizaIntensidade(intensidade);
				notas.add(new Nota(nota, Constantes.getTempo(duracao),
						intensidade));
			}
		}

		int numeroDeNotas = notas.getArray().size();
		int notasPorFaixa = Math.round(numeroDeNotas / numeroDeFaixas);

		// Laço que pega todas as notas da imagem e divide em faixas
		// simultaneas.
		int o = 0;
		for (int m = 0; m < numeroDeFaixas; m++) {
			Notas novo = new Notas();
			for (int n = 0; n < notasPorFaixa; n++) {
				novo.add(notas.getArray().get(o++));
			}
			notasFaixas.add(novo);
		}

		return new Player(notasFaixas, instrumentos);
	}

	public static Player mapeamentoIota(WritableRaster raster, int fator,
			ArrayList<Integer> instrumentos) {
		// Largura da imagem:
		int numeroDeColunas = raster.getWidth();

		// Altura da imagem:
		int numeroDeLinhas = raster.getHeight();

		// Valores temporários para soma:
		int nota, duracao, intensidade;

		// Variável brutal zeta:
		int zeta = (fator * 2) + 1;

		// Quantidade de faixas simultâneas:
		int numeroDeFaixas = instrumentos.size();

		// Variável temporária que recebe a informação do pixel:
		float[] rgb = new float[3];

		Random random = new Random();

		// Lista com as faixas simultâneas:
		ArrayList<Notas> notasFaixas = new ArrayList<Notas>();

		// Lista com as notas de uma faixa:
		Notas notas = new Notas();

		// Laço que varre cada pixel da imagem e garante que não tente varrer
		// fora da área da imagem, mesmo deixando de pegar valores:
		int limiteLinhas = (int) Math.round(numeroDeLinhas / zeta);
		int limiteColunas = (int) Math.round(numeroDeColunas / zeta);

		for (int i = 0; i < limiteLinhas; i = i + fator) {
			for (int j = 0; j < limiteColunas; j = j + fator) {
				nota = 0;
				duracao = 0;
				intensidade = 0;

				// Laço que varre a vizinhança do pixel:
				for (int k = 0; k < zeta; k++)
				{
					for (int l = 0; l < zeta; l++)
					{
						raster.getPixel(k + i, l + j, rgb);
						nota += rgb[0];
						duracao += rgb[1];
						intensidade += rgb[2];
					}
				}

				// Cálculo da média da vizinhança
				int divisor = (zeta) * (zeta);
				nota = Math.round(nota / divisor);
				duracao = Math.round(duracao / divisor);
				intensidade = Math.round(intensidade / divisor);

				// Normaliza os valores para áudio:
				nota = normalizaNota(nota);
				duracao = 1 + random.nextInt(6);
				intensidade = normalizaIntensidade(intensidade);
				notas.add(new Nota(nota, Constantes.getTempo(duracao),
						intensidade));
			}
		}

		int numeroDeNotas = notas.getArray().size();
		int notasPorFaixa = Math.round(numeroDeNotas / numeroDeFaixas);

		// Laço que pega todas as notas da imagem e divide em faixas
		// simultaneas.
		int o = 0;
		for (int m = 0; m < numeroDeFaixas; m++)
		{
			Notas novo = new Notas();
		
			for (int n = 0; n < notasPorFaixa; n++)
			{
				novo.add(notas.getArray().get(o++));
			}
			notasFaixas.add(novo);
		}
		return new Player(notasFaixas, instrumentos);
	}

	/*
	 * Métodos de normalização fazem a conversão proporcional dos valores RGB
	 * (0~255) para os valores de intensidade, duração e nota.
	 */
	private static int normalizaIntensidade(int intensidade) {
		return (int) Math.round(intensidade * 0.44);
	}

	private static int normalizaDuracao(int duracao) {
		return (int) Math.round(duracao / 42.5);
	}

	private static int normalizaNota(int nota) {
		return (int) Math.round(30 + nota * 0.235);
	}
	
	public static int rgbParaAltura(int rgb)
	{
		return rgb * 90 / 255 + 30;
	}

	public static Player Teste()
	{
		Random r = new Random();

		int[] notes = new int[] { 60, 62, 63, 65, 67, 68, 71, 72, 72, 71, 69,
				67, 65, 64, 62, 60 };

		Notas notas1 = new Notas();
		Notas notas2 = new Notas();
		Notas notas3 = new Notas();

		for (int nota : notes)
		{
			notas1.add(new Nota(nota, Constantes.COLCHEIA, 70));
			notas2.add(new Nota(30 + r.nextInt(30), Constantes.SEMINIMA, 70));
			notas3
					.add(new Nota(90 + r.nextInt(30), Constantes.SEMICOLCHEIA,
							70));
		}

		ArrayList<Notas> notasFaixas = new ArrayList<Notas>();
		notasFaixas.add(notas1);
		notasFaixas.add(notas2);
		notasFaixas.add(notas3);

		ArrayList<Integer> instrumentos = new ArrayList<Integer>();
		instrumentos.add(0);
		instrumentos.add(70);
		instrumentos.add(40);

		new Writer("C:/temp/", "teste.mmf", notasFaixas, instrumentos);

		// Abre o arquivo MMF com a m?sica :c)
		Reader reader = new Reader("C:/temp/", "teste.mmf");
		// Inicializa o player:
		return new Player(reader.getNotasFaixas(), reader.getInstrumentos());
	}
}
